﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using UMLObjects;
using System.Collections.ObjectModel;
using Commands;
using UMLUserControls;
using System.Threading;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;
using Microsoft.Win32;
using System.Printing;
using System.Windows.Xps;



namespace WIN_UML_Editor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        //Create new activity diagram
        ActivityDiagram MainDiagram = new ActivityDiagram();

        //Global UndoService
        UndoService US = new UndoService();

        //Global canvas
        public UMLCanvas GlobalCanvas;

        //Prepare to print
        PrintDialog printDialog = new PrintDialog();

        public MainWindow()
        {
            InitializeComponent();
            // Setting datacontext to make Focus-binding possible
            this.DataContext = UMLFocus.Instance;

            //Initialise canvas
            GlobalCanvas = new UMLCanvas(US,MainDiagram);
            
            //Colouring the canvas
            LinearGradientBrush myLinearGradientBrush = new LinearGradientBrush();
            myLinearGradientBrush.StartPoint = new Point(0, 0);
            myLinearGradientBrush.EndPoint = new Point(1, 1);
            myLinearGradientBrush.GradientStops.Add(
                new GradientStop(Colors.LightSteelBlue, 0.0));
            myLinearGradientBrush.GradientStops.Add(
                new GradientStop(Colors.CornflowerBlue, 1.1));

            drawGrid.Background = myLinearGradientBrush;
            //End colouring

            //Colouring workspace with tiles
            ImageBrush gridTile = new ImageBrush();
            gridTile.ImageSource =
                new BitmapImage(
                    new Uri("../../Images/bgtile.png", UriKind.Relative)
                );

            gridTile.TileMode = TileMode.Tile;
            gridTile.ViewportUnits = BrushMappingMode.Absolute;
            gridTile.Viewport = new Rect(0, 0, 10, 10);
            GlobalCanvas.Background = gridTile;
            //End colouring workspace

            //Define printable area
            CanvasBorder.Width = printDialog.PrintableAreaWidth+2;
            CanvasBorder.Height = printDialog.PrintableAreaHeight+2;
            CanvasBorder.Margin = new Thickness(50, 20, 50, 20);

            // Set canvas size to the printing dimensions.
            GlobalCanvas.Width = printDialog.PrintableAreaWidth;
            GlobalCanvas.Height = printDialog.PrintableAreaHeight;
            
            //Add to GlobalCanvas
            drawGrid2.Children.Add(GlobalCanvas);
        }

        //Function to clear and redraw everything on drawboard.
        private void clearAndRedraw()
        {
            //Delete everything on drawboard
            GlobalCanvas.Children.Clear();

            //Draw activities
            foreach(Activity Act in MainDiagram.Activities){
                var UC = new UCActivity(Act);
                Canvas.SetTop(UC, Act.Position.Y);
                Canvas.SetLeft(UC, Act.Position.X);
                GlobalCanvas.Children.Add(UC);
            }

            //Draw initial states
            foreach (InitialState state in MainDiagram.InitialStates)
            {
                var UC = new UCInitialState(state);
                Canvas.SetTop(UC, state.Position.Y);
                Canvas.SetLeft(UC, state.Position.X);
                GlobalCanvas.Children.Add(UC);
            }

            //Draw decisions
            foreach (Decision dec in MainDiagram.Decisions)
            {
                var UC = new UCDecision(dec);
                Canvas.SetTop(UC, dec.Position.Y);
                Canvas.SetLeft(UC, dec.Position.X);
                GlobalCanvas.Children.Add(UC);
            }

            //Draw final states
            foreach (FinalState state in MainDiagram.FinalStates)
            {
                var UC = new UCFinalState(state);
                Canvas.SetTop(UC, state.Position.Y);
                Canvas.SetLeft(UC, state.Position.X);
                GlobalCanvas.Children.Add(UC);
            }

            //Update sizes for arrow offset
            GlobalCanvas.UpdateLayout();

            //Create arrows
            foreach (Arrow arrow in MainDiagram.Arrows)
            {
                IArrowedUC toUC = null;
                IArrowedUC fromUC = null;
                UCArrow arrowUC;
                
                //Connect arrows and objects
                foreach (UIElement element in GlobalCanvas.Children)
                {
                    if (element is IArrowedUC)
                    {
                        IUMLObject umlobj = ((IArrowedUC)element).getObject();
                        if (arrow.PointsTo == umlobj)
                            toUC = element as IArrowedUC;
                        if (arrow.PointsFrom == umlobj)
                        {
                            fromUC = element as IArrowedUC;
                        }
                    }
                }

                //If both a from and to object is found, draw arrow
                if (toUC != null && fromUC != null)
                {
                    arrowUC = new UCArrow(arrow, toUC, fromUC);
                    toUC.AddArrowTo(arrowUC);
                    fromUC.AddArrowFrom(arrowUC);
                    arrowUC.UpdateOffsets();
                    GlobalCanvas.Children.Add(arrowUC);
                }
            }

        }
        
        //Save the activity diagram
        private void saveDiagram(ActivityDiagram MainDiagram, String location)
        {
            using (FileStream writer = new FileStream(location,FileMode.Create, FileAccess.Write))
            {
                //Creates dataserializer.. 
                //Please note the only important argument after typeof(), "true", which indicates that the objectreferences are maintained
                DataContractSerializer ser = new DataContractSerializer(typeof(ActivityDiagram),null,999999,false,true,null);
                ser.WriteObject(writer, MainDiagram);
            }
            
        }

        //Load an acitivity diagram
        private ActivityDiagram loadDiagram(String location)
        {
            ActivityDiagram diagram = new ActivityDiagram();

            using (FileStream reader = new FileStream(location, FileMode.Open, FileAccess.Read))
            {
                DataContractSerializer ser = new DataContractSerializer(typeof(ActivityDiagram));
                diagram = (ActivityDiagram)ser.ReadObject(reader);
            }
            
            return diagram;
        }
        //Load dialog to initialise loadDiagram(String location)
        private void loadDialog(object sender, ExecutedRoutedEventArgs e)
        {
            // Create an instance of the open file dialog box.
            OpenFileDialog openFileDialog1 = new OpenFileDialog();

            // Set filter options and filter index.
            openFileDialog1.Filter = "Diagrams (.dia)|*.dia|All Files (*.*)|*.*";
            openFileDialog1.FilterIndex = 1;
            openFileDialog1.Multiselect = false;

            // Call the ShowDialog method to show the dialog box.
            bool? userClickedOK = openFileDialog1.ShowDialog();

            // Process input if the user has clicked OK.
            if (userClickedOK == true)
            {
                // Open the selected file to read.
                String location = openFileDialog1.FileName;
                MainDiagram = loadDiagram(location);
                GlobalCanvas.MainDiagram = MainDiagram;
                US.ClearHistory();
                clearAndRedraw();
            }
        }

        //Save dialog to save diagram
        private void saveDialog(object sender, ExecutedRoutedEventArgs e)
        {
            Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
            dlg.FileName = "NewDiagram"; // Default file name
            dlg.DefaultExt = ".dia"; // Default file extension
            dlg.Filter = "Diagrams (.dia)|*.dia"; // Filter files by extension

            // Show save file dialog box
            Nullable<bool> result = dlg.ShowDialog();

            // Process save file dialog box results
            if (result == true)
            {
                saveDiagram(MainDiagram, dlg.FileName); // Save document
                MessageBox.Show("Diagram successfully Saved!");
            }
        }

        //Creates new diagram
        private void newDialog(object sender, ExecutedRoutedEventArgs e)
        {
            if(System.Windows.Forms.MessageBox.Show("Do you really want to create a new file? Unsaved items will be lost.", "Create new diagram", System.Windows.Forms.MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
            {
                MainDiagram = new ActivityDiagram();
                GlobalCanvas.Children.Clear();
                GlobalCanvas.MainDiagram = MainDiagram;
                US.ClearHistory();
            }
        }

        //Adds new activity to current diagram
        private void AddActivityExecuted(object sender, RoutedEventArgs e)
        {
            Activity act = new Activity();
            act.Position = new Point(10, 10);
            var UC = new UCActivity(act);

            //Start position
            Canvas.SetTop(UC, act.Position.Y);
            Canvas.SetLeft(UC, act.Position.X);

            GlobalCanvas.addAcitivityUC(UC); //Add the object
        }

        //Undo current action
        private void UndoExecuted(object sender, RoutedEventArgs e)
        {
            US.Undo();
        }

        //Checks if undo is possible
        private void UndoExecutedCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = US.CanUndo;
            e.ContinueRouting = false;
            e.Handled = true;
        }

        //Redoes an undo
        private void RedoExecuted(object sender, RoutedEventArgs e)
        {
            US.Redo();
        }

        //Checks if a redo is possible
        private void RedoExecutedCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = US.CanRedo;
            e.ContinueRouting = false;
            e.Handled = true;
        }

        //Checks if possible to add an arrow
        private void AddArrowCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            Boolean canArrow = false;
            IUMLUC focus = UMLFocus.Instance.Object;
            if (focus != null && focus is IArrowedUC)
            {
                    canArrow = ((IArrowedUC)focus).canSendArrow(); //Checks if object violates with the activity diagram standard. For instance a final state cannot add an arrow.
            }
            e.CanExecute = canArrow;
            e.ContinueRouting = false;
            e.Handled = true;
        }

        //Adds arrow
        private void AddArrowExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            IUMLUC focus = UMLFocus.Instance.Object;
            if (focus != null && focus is IArrowedUC)
            {
                GlobalCanvas.ActivateArrowCreator((IArrowedUC)focus);
            }
        }

        //Checks if an object is selected
        private void DeleteElementCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (UMLFocus.Instance.Object != null)
            {
                e.CanExecute = true;
                e.ContinueRouting = false;
                e.Handled = true;
            }
        }

        //Deletes object
        private void DeleteElementExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            UMLFocus.Instance.Object.deselect();
            DeleteElementCommand cmd = new DeleteElementCommand(GlobalCanvas, (UIElement)UMLFocus.Instance.Object);
            US.Execute(cmd);

        }

        //Print UML diagram
        protected void UMLPrint(object sender, RoutedEventArgs e)
        {
            
            if (printDialog.ShowDialog() == true)
            {
            printDialog.PrintVisual(GlobalCanvas, "UML Document");
            }    
        }

        //Adds a decision
        private void AddDecisionExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            Decision decision = new Decision();
            decision.Position = new Point(10, 10);
            var UC = new UCDecision(decision);

            //Start position
            Canvas.SetTop(UC, decision.Position.Y);
            Canvas.SetLeft(UC, decision.Position.X);

            GlobalCanvas.addDecisionUC(UC); //Add decision
        }

        //Adds initial state
        private void AddInitialStateExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            InitialState state = new InitialState();
            state.Position = new Point(10, 10);
            var UC = new UCInitialState(state);

            //Start position
            Canvas.SetTop(UC, state.Position.Y);
            Canvas.SetLeft(UC, state.Position.X);

            GlobalCanvas.addInitialStateUC(UC); //Add initial state
        }

        //Adds final state
        private void AddFinalStateExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            FinalState state = new FinalState();
            state.Position = new Point(10, 10);
            var UC = new UCFinalState(state);

            //Start position
            Canvas.SetTop(UC, state.Position.Y);
            Canvas.SetLeft(UC, state.Position.X);

            GlobalCanvas.addFinalStateUC(UC); //Add final state
        }

        //Opens help dialog
        private void helpExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("Press on the object type you want to add.\n\n"
            + "There are 4 different types of objects: Initial state, Activities, Decisions and Final state.\n\n"
            + "From Initial State, Activities and Decisions you can add arrows to other objects by clicking the add arrrow button.\n\n"
            +"If you cannot add an arrow, then you are violating the Activity Diagram Standard and are therefore not meant to be able to add an arrow.","HELP");
        }

        //Opens about dialog
        private void aboutExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            
            MessageBox.Show("Version 1.00\n\n"
                +"The WIN UML Editor was created as a part of the course 02350 - Windows Programming on DTU.\n\n"
                +(char)64+" 2010 Jannik Boll Nielsen, Jonathen Dyssel Stets, Kinna Majvig Knudsen og Lasse Korff","About WIN UML Editor");
        }

        //Exit command
        private void exitExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            //Ask if user wants to save before exiting
            if (System.Windows.Forms.MessageBox.Show("Do you want to save the application before exiting?", "EXIT", System.Windows.Forms.MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
                saveDialog(sender, e);

            Environment.Exit(0); //Exit
        }


    }
}
